//
// Created by guoshunw on 24-3-26.
//
#include "log.h"
#include "thread"
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <iostream>

#include "app_context.h"

//#define TEST_OMX
#ifdef TEST_OMX
#include "OMX_Core.h"
#define COMPONENT_NAME_BASE "OMX.st.alsa.alsasink"
#define BASE_ROLE "alsa.alsasink"
#define AUDIO_EFFECT "OMX.st.volume.component"

#include "enum_to_string.h"

OMX_ERRORTYPE test_OMX_ComponentNameEnum() {
    OMX_ERRORTYPE err;
    char name[OMX_MAX_STRINGNAME_SIZE];
    for (auto index = 0; OMX_ErrorNone == (err = OMX_ComponentNameEnum(name, OMX_MAX_STRINGNAME_SIZE, index)); ++index) {
        LOGI << "Component " << index << ": " << name;
    }
    return err;
}
OMX_ERRORTYPE test_OMX_RoleEnum(OMX_STRING component_name) {
    LOGI << "Getting roles of component " << component_name << ".";
    OMX_U32 no_of_roles = 0;
    auto err = OMX_GetRolesOfComponent(component_name, &no_of_roles, nullptr);
    if (err != OMX_ErrorNone) {
        LOGE << "Failed to get roles of component " << component_name << ". Error: " << err;
        return err;
    }
    if (no_of_roles == 0) {
        LOGI << "The Number of roles is 0." << std::endl
             << "The component " << component_name << " selected is not correct for the purpose of this test." << std::endl
             << "Exiting...";
        return err;
    }
    LOGI << "Number of roles: " << no_of_roles;
    OMX_U8 *string_of_roles[no_of_roles];
    for (auto &p: string_of_roles) {
        p = (OMX_U8 *) alloca(OMX_MAX_STRINGNAME_SIZE);
    }

    err = OMX_GetRolesOfComponent(component_name, &no_of_roles, string_of_roles);
    if (err != OMX_ErrorNone) {
        LOGE << "Failed to get roles of component " << component_name << ". Error: " << err;
        return err;
    }
    LOGI << "Roles of component " << component_name << ":";
    for (auto i = 0; i < no_of_roles; ++i) {
        LOGI << "The role " << i << ": " << string_of_roles[i];
    }
    return err;
}

OMX_ERRORTYPE test_OMX_ComponentEnumByRole(OMX_STRING role_name) {
    LOGI << "Getting number of components per role for " << role_name;

    OMX_U32 no_of_comp_per_role = 0;
    OMX_ERRORTYPE err = OMX_GetComponentsOfRole(role_name, &no_of_comp_per_role, nullptr);
    if (err != OMX_ErrorNone) {
        LOGE << "Failed to get components of role " << role_name << ". Error: " << err;
        return err;
    }

    LOGI << "Number of components per role for " << role_name << " is " << no_of_comp_per_role;
    OMX_U8 *string_of_com_per_rule[no_of_comp_per_role];
    for (auto &p: string_of_com_per_rule) {
        p = (OMX_U8 *) alloca(OMX_MAX_STRINGNAME_SIZE);
    }

    err = OMX_GetComponentsOfRole(role_name, &no_of_comp_per_role, string_of_com_per_rule);
    if (err != OMX_ErrorNone) {
        LOGE << "Failed to get components of role " << role_name << ". Error: " << err;
        return err;
    }

    LOGI << "Components of role " << role_name << ":";
    for (const auto &p: string_of_com_per_rule) {
        LOGI << p;
    }
    return err;
}

// https://github.com/felipec/libomxil-bellagio/blob/master/test/components/video/omxvideoenctest.c
int omx_entry() {
    std::string info = "Init the Omx core";
    auto err = OMX_Init();
    if (err != OMX_ErrorNone) {
        info += " failed: " + std::to_string(err);
        LOGE << info;
        return -1;
    }
    LOGI << info + " succeeded.";
    LOGI << "----------------------------------------------------";
    err = test_OMX_ComponentNameEnum();
    //        LOGI << "err: " << std::hex << std::showbase << err;
    auto comp_name = const_cast<OMX_STRING>(COMPONENT_NAME_BASE);
    LOGI << "----------------------------------------------------";
    test_OMX_RoleEnum(comp_name);
    LOGI << "----------------------------------------------------";
    test_OMX_ComponentEnumByRole(const_cast<OMX_STRING>(BASE_ROLE));
    LOGI << "OMX de-initialized...";
    OMX_Deinit();
    return 0;
}
// https://github.com/felipec/libomxil-bellagio

#endif
int main(int argc, char *argv[]) {
    google::InitGoogleLogging(argv[0]);
    google::InstallFailureSignalHandler();
    google::ParseCommandLineFlags(&argc, &argv, true);
#ifdef TEST_OMAX
    //    omx_entry();
#else
    //        LOGI << GetEnumName(OMX_ErrorNone);
    //    return 0;
    openmax::test::AppContext app;
    app.Start();

    atexit([]() {
        google::ShutdownGoogleLogging();
    });

    std::jthread feed_thread([](const std::stop_token &stoken, openmax::test::AppContext *app) -> auto {
        long ret{};
        while (true) {
            ret = app->SyncFillBuffer();
            if (-1 == ret) {
                app->SetEos(OMX_TRUE);
                ret = 0;
                break;
            }
            if (stoken.stop_requested()) {
                LOGI << "feed thread: " << std::this_thread::get_id() << " is requested to stop....";
                ret = -1L;
                app->SetEos(OMX_TRUE);
                break;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        LOGI << "feed thread: " << std::this_thread::get_id() << " exit ret: " << ret << ".";
        return ret;
    },
                             &app);


    std::cout << "Enter 'Q' or 'q' to quit the app: ";
    while (true) {
        if ('Q' == std::toupper(getchar())) {
            LOGI << "User quit the app, stopping playback...";
            //            app.SetEos(OMX_TRUE);
            //            feed_thread.get_stop_source().request_stop();
            feed_thread.request_stop();
            //            if(feed_thread.joinable())feed_thread.join(); // 😀
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
            break;
        }
    }
    app.Stop();

    LOGI << "main thread: " << std::this_thread::get_id() << " exit.";
#endif
    return 0;
}